----------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity alu is
Port (	Clk			: in	STD_LOGIC;
		Control		: in	STD_LOGIC_VECTOR (5 downto 0);
		Operand1	: in	STD_LOGIC_VECTOR (31 downto 0);
		Operand2	: in	STD_LOGIC_VECTOR (31 downto 0);
		Result1		: out	STD_LOGIC_VECTOR (31 downto 0);
		Result2		: out	STD_LOGIC_VECTOR (31 downto 0);
		Debug		: out	STD_LOGIC_VECTOR (31 downto 0));
end alu;

architecture Behavioral of alu is
	
	-----------------------------------------
	---------- COMPONENT DECLARATIONS--------
	-----------------------------------------
	component Unsigned_32x32_mult
		PORT (
		clk	:	in	STD_LOGIC;
		arg1 	: 	in STD_LOGIC_VECTOR (31 downto 0);
		arg2 	: 	in STD_LOGIC_VECTOR (31 downto 0);
		start_signal : in STD_LOGIC;
		result : out STD_LOGIC_VECTOR (63 downto 0);
		is_signed : IN STD_LOGIC
		);
			
	end component;
		
	component divider_signed is
	PORT (
	clk : in STD_LOGIC;
	start_division : in STD_LOGIC;
	numerator : in STD_LOGIC_VECTOR(31 downto 0);
	denominator : in STD_LOGIC_VECTOR(31 downto 0);
	is_signed : in STD_LOGIC; -- 1 for signed, 0 for unsigned
	
	quotient : out STD_LOGIC_VECTOR (31 downto 0);
	remainder : out STD_LOGIC_VECTOR (31 downto 0);
	
	div_0_flag : out STD_LOGIC;
	overflow_flag : out STD_LOGIC
	);
	end component;
	
	component add_sub is
		PORT (
		ADD_SUB_CONTROL : in STD_LOGIC;
		is_signed: in STD_LOGIC; -- whether signed operation or not
		is_slt : in STD_LOGIC;
		
		A : in STD_LOGIC_VECTOR(31 downto 0); -- Operand 1
		B : in STD_LOGIC_VECTOR(31 downto 0); -- Operand 2
		
		result : out STD_LOGIC_VECTOR(31 downto 0); -- Out result
		
		carry_flag : out STD_LOGIC;
		overflow_flag : out STD_LOGIC
		
		);
	end component;
	
	component logic is
		PORT (
		OP: in std_logic_vector(2 downto 0);

		A: in std_logic_vector(31 downto 0);
		B: in std_logic_vector(31 downto 0);

		C: out std_logic_vector(31 downto 0)
		);
	end component;
	
	component shifter is
		PORT (
		OP: in std_logic_vector(1 downto 0); -- higher bit represents "right", lower bit represents 'logical'

		A: in std_logic_vector(31 downto 0);
		B: in std_logic_vector(31 downto 0);

		C: out std_logic_vector(31 downto 0);
		overflow_flag : out std_logic
		);
	end component;

	-----------------------------------------
	----------SIGNAL DECLARATION-------------
	-----------------------------------------

	-- multiply signals
	signal start_the_mult : STD_LOGIC := '0';
	signal multres : STD_LOGIC_VECTOR(63 downto 0);
	signal mult_signed : STD_LOGIC := '0';
	
	--- divide signals
	signal start_the_div : STD_LOGIC := '0';
	signal div_quotient : STD_LOGIC_VECTOR(31 downto 0);
	signal div_remainder : STD_LOGIC_VECTOR(31 downto 0);
	signal div_signed : STD_LOGIC;
	signal div_div_0_flag : STD_LOGIC;
	signal div_overflow_flag : STD_LOGIC;
	
	signal mult_div_input1 : STD_LOGIC_VECTOR(31 downto 0);
	signal mult_div_input2 : STD_LOGIC_VECTOR(31 downto 0);
	
	signal mult_div_sign : STD_LOGIC;
	
	signal mult_div_reg1 : STD_LOGIC_VECTOR(31 downto 0);
	signal mult_div_reg2 : STD_LOGIC_VECTOR(31 downto 0);
	
	signal mult_div_sign_reg : STD_LOGIC;
	
	-- adder/subtracter signals
	signal add_sub_control : STD_LOGIC;
	signal adder_result : STD_LOGIC_VECTOR(31 downto 0);
	signal adder_is_slt : STD_LOGIC;
	signal adder_carry : STD_LOGIC;
	signal adder_overflow : STD_LOGIC;
	signal adder_is_signed : STD_LOGIC;
	
	-- logic unit
	signal logic_op : STD_LOGIC_VECTOR(2 downto 0);
	signal logic_out : STD_LOGIC_VECTOR(31 downto 0);
	
	-- shifter unit
	signal shift_op : STD_LOGIC_VECTOR(1 downto 0);
	signal shifter_out : STD_LOGIC_VECTOR(31 downto 0);
	signal shifter_overflow : STD_LOGIC;
	
	--- internal storage/state
	type result_source_type is (arith, divide, multiply, logik, shift, rst_state);
	
	-- Max number of clocks allowed for a single instruction = 63. This MAY most likely result in unused FFs
	signal countdown : STD_LOGIC_VECTOR(5 downto 0) := "000000"; 
	
	-- result buffer
	signal result_buf1 : STD_LOGIC_VECTOR(31 downto 0);
	signal result_buf2 : STD_LOGIC_VECTOR(31 downto 0);
	-- flags buffer
	signal flags_buf : STD_LOGIC_VECTOR(2 downto 0);
	-- branch signal
	signal branch_signal : STD_LOGIC;
	
	-- HI and LO storage
	signal HI_LO : STD_LOGIC_VECTOR(63 downto 0) := X"0000000000000000";
	
begin
	
	mult_div_input1 <= operand1 when Control(4 downto 1) = "1100" or Control(4 downto 1) = "1110" else mult_div_reg1;
	mult_div_input2 <= operand2 when Control(4 downto 1) = "1100" or Control(4 downto 1) = "1110" else mult_div_reg2;
	
	mult_div_sign <= Control(0) when countdown = "000000" else mult_div_sign_reg;
	
	-----------------------------------------
	----------COMPONENT INSTANTIATION--------
	-----------------------------------------
	
	-- main multiplier unit
	mymult : unsigned_32x32_mult 
	port map 
	(
		clk => clk,
		arg1 => mult_div_input1,
		arg2 => mult_div_input2,
		result => multres,
		start_signal => start_the_mult,
		is_signed => mult_signed
	);
	
	-- main divider unit
	mydiv : divider_signed
	PORT map (
		clk => clk,
		start_division => start_the_div,
		numerator => mult_div_input1,
		denominator => mult_div_input2,
		is_signed => div_signed,
		
		quotient => div_quotient,
		remainder => div_remainder,
		
		div_0_flag => div_div_0_flag,
		overflow_flag => div_overflow_flag
	);

	-- combinatorial adder
	myadd : add_sub
	port map
	(
		ADD_SUB_CONTROL => add_sub_control,
		is_signed => adder_is_signed,
		is_slt => adder_is_slt,
		
		A => operand1,
		B => operand2,
		
		result => adder_result,
		carry_flag => adder_carry,
		overflow_flag => adder_overflow
	);
	
	-- main unit handling logical operations
	mylogic : logic
	PORT map(
		OP => logic_op,

		A => operand1,
		B => operand2,

		C => logic_out
	);

	-- main shifter unit
	myshifter : shifter
	PORT MAP(
		OP => shift_op,

		A => operand1,
		B => operand2,

		C => shifter_out,
		overflow_flag => shifter_overflow
	);
	
	-----------------------------------------
	---------- SIGNAL DECLARATION -----------
	-----------------------------------------
	
	mult_signed <= mult_div_sign;
	div_signed <= mult_div_sign;
	
	-- result1 <= Result_buf1;
	-- result2 <= Result_buf2;
	
	debug <= x"0000000"&branch_signal&flags_buf;
	
	
	ADD_SUB_CONTROL <= control(2);
	adder_is_slt <= control(1);
	adder_is_signed <= control(0);
	
	logic_op <= Control(2 downto 0);
	
	shift_op <= Control(2 downto 1);
	-------------------------------------

	-- Synchronous version, multiplexer to pull results form the correct units
	process(Control, adder_result, shifter_out, logic_out, HI_LO) 
	begin 
		branch_signal <= '0'; -- just dummy
		if (Control(4 downto 3) = "01") then -- adder-related
			Result1 <= adder_result;
			Result2 <= X"00000000";	
		elsif (Control (4 downto 3) = "10") then -- shifter-related
			-- Shifting unit
			Result1 <= shifter_out;
			Result2 <= X"00000000";		
		elsif (Control(4 downto 3) = "00") then -- logic-related
			-- Logic Unit
			Result1 <= logic_out;
			Result2 <= X"00000000";
		elsif (Control(4 downto 3) = "11") then -- Mult/div related
			if (Control(2 downto 0) = "111") then -- HI load
				Result1 <= HI_LO(63 downto 32);
				Result2 <= X"00000000";
			elsif (Control(2 downto 0) = "110") then -- LO load
				Result1 <= HI_LO(31 downto 0);
				Result2 <= X"00000000";
			else
				-- Mult/Div unit...
				Result1 <= X"00000000";
				Result2 <= X"00000000";
			end if;
		else -- anything else is regarded as a NOP
			-- Do nothing case
			Result1 <= X"00000000";
			Result2 <= X"00000000";
		end if;
	end process;
		
	-----------------------------------------
	---------- PROCESS DECLARATION ----------
	-----------------------------------------
	process(Control, countdown, multres, div_quotient, div_remainder, logic_out, shifter_out, adder_result)
	begin
		if (Control(4 downto 1) = "1100") then
			start_the_mult <= '1';
			start_the_div <= '0';
		elsif (Control(4 downto 1) = "1110") then
			start_the_div <= '1';
			start_the_mult <= '0';
		else
			start_the_div <= '0';
			start_the_mult <= '0';
		end if;
	
	end process;
	
	-----------------------------------------
	---------- SYNCHRONOUS PORTION ----------
	-----------------------------------------
	process (Clk)
		variable result_source : result_source_type := multiply;
	begin  
		if (Clk'event and Clk = '1') then
			if (countdown = "000001") then	
				-- if Control(5) = '1' then
					-- flags_buf <= (flags_buf'length-1 downto 0 => '0');
				if (result_source = multiply) then
					HI_LO <= multres;
				elsif (result_source = divide) then
					-- Set hi low based on remainder and quotient
					HI_LO <= div_remainder & div_quotient;
				end if;
			end if;
			
			
				-- Set output signal
				if (Control(4 downto 1) = "1100") then -- multiply
					result_source := multiply;					
					countdown <= STD_LOGIC_VECTOR(to_unsigned(4, countdown'length)); -- 4 more cycles till result appears
					mult_div_reg1 <= operand1;
					mult_div_reg2 <= operand2;
					mult_div_sign_reg <= Control(0);
				elsif (Control(4 downto 1) = "1110") then
					result_source := divide;
					countdown <= STD_LOGIC_VECTOR(to_unsigned(17, countdown'length));
					mult_div_reg1 <= operand1;
					mult_div_reg2 <= operand2;
					mult_div_sign_reg <= Control(0);
				else
				
					-- Decrement countdown if required (override the previous paragraph if need be.
					if (countdown /= "000000") then
						countdown <= std_logic_vector(unsigned(countdown)-1);
					end if;
			
				end if;
			
		end if;
	end process;
--				elsif (Control(4 downto 2) = "111") then -- divide
--					result_source := divide;
--					countdown <= STD_LOGIC_VECTOR(to_unsigned(17, countdown'length));
--				elsif (Control(4 downto 3) = "01") then -- add									
--					-- Reset Flags
--					flags_buf <= (flags_buf'length-1 downto 0 => '0');
--					-- Reset Branch Flag
--					branch_signal <= '0';
--					
--					Result_buf1 <= adder_result;
--					Result_buf2 <= X"00000000";
--													
--					-- set overflow and carry flags
--					flags_buf(2) <= adder_carry;
--					flags_buf(1) <= adder_overflow;
--				elsif (Control(4 downto 3) = "00") then -- logic
--					-- Reset Flags
--					flags_buf <= (flags_buf'length-1 downto 0 => '0');
--					-- Reset Branch Flag
--					branch_signal <= '0';
--				
--					-- Logic Unit
--					Result_buf1 <= logic_out;
--					Result_buf2 <= X"00000000";
--					if (control(2 downto 0) = "101" or control(2 downto 0) = "110") then
--						branch_signal <= logic_out(0); ----- 
--					end if;
--				elsif (Control (4 downto 3) = "10") then -- shift
--				
--					-- Reset Flags
--					flags_buf <= (flags_buf'length-1 downto 0 => '0');
--					-- Reset Branch Flag
--					branch_signal <= '0';
--				
--					-- Shifting unit
--					Result_buf1 <= shifter_out;
--					Result_buf2 <= X"00000000";
--					
--					flags_buf(1) <= shifter_overflow;
--				elsif (Control(5) = '1') then -- reset 
--					-- reset signal
--					-- result_source := rst_state;
--				else
--					-- undetermined control, do nothing
--				end if;
--			else
--				
--				-- countdown = 1 ==> latch just before we enter idle state
--				if (countdown = "000001") then
--					-- output answer when ready
--					flags_buf <= (flags_buf'length-1 downto 0 => '0'); -- clear flags first...
--					case (result_source) is 
--						when multiply =>
--							Result_buf1 <= multres(31 downto 0);
--							Result_buf2 <= multres(63 downto 32);
--							
--							-- no flags need to be set for multiply
--						when divide =>
--							Result_buf2 <= div_quotient;
--							Result_buf1 <= div_remainder;
--							
--							-- overflow and div0 flag may be set
--							flags_buf(0) <= div_div_0_flag;
--							flags_buf(1) <= div_overflow_flag;
--						when others =>
--							Result_buf1 <= X"00000000";
--							Result_buf2 <= X"00000000";
--					end case;
--				end if;
--			
--				--- lower countdown...
--				countdown <= STD_LOGIC_VECTOR(unsigned(countdown) - 1);
--				
--			end if;
--			
--			
--			
--		end if;
--	end process;

end Behavioral;

